package encrypt

import (
	"Hypnos-Soundcore/constants"
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/asn1"
	"encoding/pem"
	"strings"
)

// GenerateCipher 生成密钥
func GenerateCipher() (modulus, publicPem, privatePem string, err error) {
	// generate rsa key
	var privateKey *rsa.PrivateKey
	if privateKey, err = rsa.GenerateKey(rand.Reader, 1024); err == nil {
		modulus = privateKey.N.Text(16)
		if privatePem, publicPem, err = getPrivateAndPublicPemKey(privateKey); err == nil {
			if err = checkPrivatePublicKey(publicPem, privatePem); err != nil {
				publicPem = ""
				privatePem = ""
				modulus = ""
			}
		}
	}
	return
}

// 获得公私钥pem值
func getPrivateAndPublicPemKey(prikey *rsa.PrivateKey) (pripem string, pubpem string, err error) {
	derStream := marshalPKCS8PrivateKey(prikey)
	block := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: derStream,
	}
	buf := bytes.NewBuffer([]byte{})
	if err = pem.Encode(buf, block); err == nil {
		pripem = buf.String()
	}

	// 生成公钥文件
	var derPkix []byte
	publicKey := &prikey.PublicKey
	if derPkix, err = x509.MarshalPKIXPublicKey(publicKey); err == nil {
		block = &pem.Block{
			Type:  "PUBLIC KEY",
			Bytes: derPkix,
		}
		pubbuf := bytes.NewBuffer([]byte{})
		if err = pem.Encode(pubbuf, block); err == nil {
			pubpem = pubbuf.String()
		}
	}
	return

}

// 私钥解析
func marshalPKCS8PrivateKey(key *rsa.PrivateKey) []byte {
	info := struct {
		Version             int
		PrivateKeyAlgorithm []asn1.ObjectIdentifier
		PrivateKey          []byte
	}{}
	info.Version = 0
	info.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
	info.PrivateKeyAlgorithm[0] = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
	info.PrivateKey = x509.MarshalPKCS1PrivateKey(key)
	k, _ := asn1.Marshal(info)
	return k
}

// 校验生成公私钥是否匹配
func checkPrivatePublicKey(publicPem, privatePem string) (err error) {
	src := "1234567890"
	rsaTool = &RSASecurity{}
	_ = rsaTool.SetPrivateKey(privatePem)
	_ = rsaTool.SetPublicKey(publicPem)
	var enby []byte
	var deby []byte
	if enby, err = rsaTool.PubKeyEncrypt([]byte(src)); err == nil {
		if deby, err = rsaTool.PriKeyDecrypt(enby); err == nil &&
			strings.Compare(src, string(deby)) != 0 {
			err = constants.ErrInvalidRsaKeys
		}
	}
	return
}
